package com.code.wordhelper.utils;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 处理循环段落
 */
public class CirculateTemplateUtils {
    private final static String CIRCULATE_START = "{{}}";
    private final static String CIRCULATE_END = "{{/}}";
    private final static String PLACE_HOLDER_SUFFIX = "}";
    private final static String MULTI_FIELD_SEPERATOR = ".";
    private final static String MULTI_FIELD_SEPERATOR_SPLIT = "\\.";
    private final static String INDEX_SEPERATOR_SPLIT = "\\w+\\[(\\d+)\\]";
    private final static Pattern CIRCULATE_START_PATTERN = Pattern.compile("\\{\\{(\\w+)\\}\\}");
    private final static Pattern CIRCULATE_END_PATTERN = Pattern.compile("\\{\\{\\/(\\w+)\\}\\}");

    private static Logger logger = LoggerFactory.getLogger(WordTemplateUtils.class);

    public static void handleCirculates(XWPFDocument document, Map<String, Object> dataMap) throws Exception {
        List<IBodyElement> bodyElements = document.getBodyElements();

        int start = -1;
        int end = -1;
        int cycleNum = -1;
        String startFlag = null;
        String endFlag = null;
        for (int i = 0; i < bodyElements.size(); i++) {
            IBodyElement bodyElement = bodyElements.get(i);
            if (bodyElement instanceof XWPFParagraph) {
                XWPFParagraph paragraph = (XWPFParagraph) bodyElement;
                if (PatternUtils.match(paragraph.getText(), CIRCULATE_START_PATTERN)) {
                    startFlag = PatternUtils.regex(paragraph.getText(), CIRCULATE_START_PATTERN, 1);
                    Object item = dataMap.get(startFlag);
                    if (item == null) {
                        continue;
                    }
                    cycleNum = Integer.parseInt(item.toString());
                    start = i;
                }
                if (PatternUtils.match(paragraph.getText(), CIRCULATE_END_PATTERN) && start != -1) {
                    endFlag = PatternUtils.regex(paragraph.getText(), CIRCULATE_END_PATTERN, 1);
                    if (!endFlag.equalsIgnoreCase(startFlag)) {
                        continue;
                    }
                    end = i;

                    handleCirculate(document, start, end, cycleNum);
                    start = -1;
                    startFlag = null;
                    endFlag = null;
                }
            }
        }
    }

    public static void handleCirculate(XWPFDocument document, int start, int end, Integer cycleNum) throws Exception {
        List<IBodyElement> bodyElements = document.getBodyElements();

        XmlCursor xmlCursor = null;
        IBodyElement endElement = bodyElements.get(end);
        if (endElement instanceof XWPFParagraph) {
            XWPFParagraph paragraph = (XWPFParagraph) endElement;
            xmlCursor = paragraph.getCTP().newCursor();
            xmlCursor.toEndToken();
            xmlCursor.toNextToken();
        }

        for (int i = 0; i < cycleNum; i++) {
            for (int j = start + 1; j < end; j++) {
                IBodyElement bodyElement = bodyElements.get(j);
                if (bodyElement instanceof XWPFParagraph) {
                    XWPFParagraph paragraph = (XWPFParagraph) bodyElement;
                    XWPFParagraph newParagraph = document.insertNewParagraph(xmlCursor);
                    cloneParagraphAndPicture(paragraph, newParagraph);
                    xmlCursor.toEndToken();
                    xmlCursor.toNextToken();
                } else if (bodyElement instanceof XWPFTable) {
                    XWPFTable table = (XWPFTable) bodyElement;
                    XWPFTable newTable = document.insertNewTbl(xmlCursor);
                    cloneTable(table, newTable);
                    xmlCursor.toEndToken();
                    xmlCursor.toNextToken();
                }
            }
        }

        //删除原来的模板元素
        for (int j = end; j >= start; j--) {
            document.removeBodyElement(j);
        }
    }

    /**
     * 复制段落，包括处理图片
     *
     * @param oldParagraph
     * @param targetParagraph
     */
    private static void cloneParagraphAndPicture(XWPFParagraph oldParagraph, XWPFParagraph targetParagraph) throws IOException, InvalidFormatException {
        CTPPr oldPPr = oldParagraph.getCTP().getPPr();

        //复制段落ppr属性
        targetParagraph.getCTP().setPPr(oldPPr);
        targetParagraph.setStyle(oldParagraph.getStyle());
        //复制段落中所有的xwpfrun
        List<XWPFRun> runs = oldParagraph.getRuns();
        for (int i = 0; i < runs.size(); i++) {
            XWPFRun oldRun = runs.get(i);
            XWPFRun newRun = targetParagraph.createRun();

            CTRPr oldCTRPr = oldRun.getCTR().getRPr();
            if (oldCTRPr != null) {
                String carStyle = oldRun.getStyle();
                newRun.setStyle(carStyle);
                newRun.getCTR().setRPr(oldCTRPr);
            }

            List<XWPFPicture> pictures = oldRun.getEmbeddedPictures();
            if (CollectionUtils.isNotEmpty(pictures)) {
                for (XWPFPicture picture : pictures) {
                    double width = picture.getWidth();
                    double depth = picture.getDepth();
                    XWPFPictureData pictureData = picture.getPictureData();
                    String fileName = pictureData.getFileName();
                    int pictureType = pictureData.getPictureType();
                    byte[] data = pictureData.getData();

                    ByteArrayInputStream bais = new ByteArrayInputStream(data);
                    newRun.addPicture(bais, pictureType, UUID.randomUUID().toString() + fileName, Units.toEMU((int) width), Units.toEMU((int) depth));
                }
            } else {
                newRun.setText(oldRun.getText(0));
            }
        }
    }

    private static void cloneTable(XWPFTable sourceTable, XWPFTable targetTable) {
//        targetTable.getCTTbl().setTblPr(sourceTable.getCTTbl().getTblPr());
        for (int i = 0; i < targetTable.getNumberOfRows(); i++) {
            targetTable.removeRow(i);
        }

        for (int i = 0; i < sourceTable.getNumberOfRows(); i++) {
            XWPFTableRow sourceRow = sourceTable.getRow(i);
            XWPFTableRow targetRow = targetTable.insertNewTableRow(i);
            targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
            List<XWPFTableCell> sourceCells = sourceRow.getTableCells();
            for (XWPFTableCell sourceCell : sourceCells) {
                XWPFTableCell newCell = targetRow.addNewTableCell();
                copyCell(sourceCell, newCell);
            }
        }
    }

    private static void copyCell(XWPFTableCell sourceCell, XWPFTableCell targetCell) {
        //段落属性
        List<XWPFParagraph> sourceCellParagraphs = sourceCell.getParagraphs();
        if (sourceCellParagraphs == null || sourceCellParagraphs.size() <= 0) {
            return;
        }

        // 设置列的样式位模板的样式
        targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
        for (int i = 0; i < targetCell.getParagraphs().size(); i++) {
            targetCell.removeParagraph(i);
        }

        for (int i = 0; i < sourceCell.getParagraphs().size(); i++) {
            XWPFParagraph sourcePar = sourceCellParagraphs.get(i);
            XWPFParagraph targetPar = targetCell.addParagraph();

            targetPar.getCTP().setPPr(sourcePar.getCTP().getPPr());

            List<XWPFRun> sourceParRuns = sourcePar.getRuns();
            if (sourceParRuns != null && sourceParRuns.size() > 0) {
                // 如果当前cell中有run
                List<XWPFRun> runs = sourcePar.getRuns();

                for (int j = 0; j < runs.size(); j++) {
                    XWPFRun run = runs.get(j);
                    XWPFRun targetRun = targetPar.insertNewRun(j);
                    targetRun.getCTR().setRPr(run.getCTR().getRPr());

                    targetRun.setText(run.getText(0), 0);
                }
            }
        }
    }

    /**
     * 复制段落
     *
     * @param oldParagraph
     * @param targetParagraph
     */
    private static void cloneParagraph(XWPFParagraph oldParagraph, XWPFParagraph targetParagraph) {
        targetParagraph.getCTP().set(oldParagraph.getCTP());
    }

    /**
     * 复制表格
     *
     * @param oldTable
     * @param targetTable
     */
    private static void cloneTableOld(XWPFTable oldTable, XWPFTable targetTable) {
        targetTable.getCTTbl().set(oldTable.getCTTbl());
    }


    public static void main(String[] args) throws Exception {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("name", "西门大桥");
        dataMap.put("age", "0000");
        dataMap.put("detectionTime", "2023-10-10");

        List<Object> objectList = new ArrayList<>();
        dataMap.put("results", objectList);
        dataMap.put("totalScore", "99.9");
        dataMap.put("totalLevel", "一类");
        dataMap.put("item", 3);

        OPCPackage pack = POIXMLDocument.openPackage("C:\\Users\\10419\\Desktop\\test.docx");
        XWPFDocument document = new XWPFDocument(pack);//创建word对象

        handleCirculates(document, dataMap);

        FileOutputStream fos = new FileOutputStream("C:\\Users\\10419\\Desktop\\test11.docx");
        document.write(fos);
        fos.close();
    }
}
